

package SSF.Net;

import java.util.*;

import SSF.Net.Util.*;
import SSF.OS.*;
import com.renesys.raceway.DML.*;
import com.renesys.raceway.SSF.*;

/** Implementation of a link layer that performs simple layer 2 routing
  * among a set of two or more attached interfaces.   Uses the non-standard
  * NEXT_HOP field in the IP header to identify the channel on which to 
  * rebroadcast each received packet. 
  */
public class lanLinkLayer extends Entity implements linkLayer {

  private inChannel LAN_IN;

  private outChannel[] LAN_OUT;

  private link forLink;

  public link onLink() { return forLink;}

  public lanLinkLayer(link L) {
    alignTo(forLink = L);
    LAN_IN = new inChannel(this);
    LAN_OUT = new outChannel[forLink.attachedCount+1];
    for (int iface=0; iface<forLink.attachedCount+1;iface++)
      LAN_OUT[iface]=null; // just to make sure
    
    for (Enumeration ends = forLink.attachedInterfaces.elements();
	 ends.hasMoreElements();) {
      try {
	NIC N = (NIC)ends.nextElement();
	Host H = (Host)N.inGraph();
	ProtocolSession IP = H.SessionForName("ip");
	N.open(IP,this);
      } catch (ProtocolException pex) {
	// if host doesn't support IP, oh well.
      }
    }

    process forwarder = new process(this) {
      public boolean isSimple() { return true; };
      public void action() {
	/*SSF.*/Event[] evts = LAN_IN.activeEvents();
	for (int e=0; e<evts.length; e++) {
	  PacketEvent pevt = (PacketEvent)evts[e];
    	  if (forLink.dumpTo!=null) forLink.dumpTo.dump(pevt);
	  int next_hop = ((IpHeader)pevt.getHeaders()).NEXT_HOP_IP;
	  int dest_ip = ((IpHeader)pevt.getHeaders()).DEST_IP;
	  outChannel xbar = LAN_OUT[(next_hop==0?dest_ip:next_hop)-forLink.ipaddr];
	  if (xbar == null) {
	    System.err.println("** Bad header: LAN dropping "+pevt);
	  } else {
	    // System.err.println("LAN forwarding "+pevt+" on "+xbar+" at "+now());
	    xbar.write(pevt,0);
	  }
	}
	waitOn(LAN_IN);
      }
    };
  }


  public void up(NIC nic) {
    nic.OUT = new outChannel(nic.inGraph(),(long)nic.delay());
    nic.OUT.mapto(LAN_IN);

    outChannel lanout = new outChannel(this,onLink().delay()); 
    LAN_OUT[nic.ipAddr-forLink.ipaddr] = lanout;
    lanout.mapto(nic.IN);
  }

  public void transmit(NIC forNIC, PacketEvent pevt, long qdelay) {
    //System.err.println(pevt+" on "+forNIC+" at "+now()+" with "+qdelay);
    forNIC.OUT.write(pevt,qdelay);	
  };

  public /*SSF.*/Event[] receive(NIC forNIC) {
    return forNIC.IN.activeEvents();
  };

  public Enumeration peers(NIC forNIC) {
    return new lanPeerEnumeration(forNIC);
  }

  class lanPeerEnumeration implements java.util.Enumeration {
    NIC nic, next; 
    Enumeration generator;
    public lanPeerEnumeration(NIC n) {
      nic = n; 
      next = null;
      generator = forLink.attachedInterfaces.elements();
      while (generator.hasMoreElements()) {
	next = (NIC)generator.nextElement();
	if (next!=nic) break; else next = null;
      }
    }
    public boolean hasMoreElements() { return (next!=null); }
    public Object nextElement() { 
      NIC rnic = next; next = null;
      while (generator.hasMoreElements()) {
	next = (NIC)generator.nextElement();
	if (next!=nic) break; else next = null;
      }
      return rnic;
    }
  }
}




/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
